package org.jef.sequence.core;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jef.core.util.SpringContextHolder;
import org.jef.sequence.entity.SysSequence;
import org.jef.sequence.mapper.SysSequenceMapper;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @version 1.0
 * @description: 公共方法
 * @author: ChengZhi 0336
 * @create: 2024-08-07 11:47
 **/
@Slf4j
@Component
@AllArgsConstructor
public class CpublicFunc {

    private final SysSequenceMapper sysSequenceMapper;
    /**
     * list中保存两个数，第一个为当前值，第二个为缓存大小
     */
    private static Map<String, Long[]> sequenceMap = new ConcurrentHashMap<String, Long[]>();

    /**
     * 获取公共so_nbr
     * @return
     */
    public synchronized Long getSoNbr() {
        return getSoNbr(SequenceEnum.PUBLIC_SEQUENCE);
    }

    /**
     * 获取指定sequence的so_nbr,
     * @param sequence
     * @return
     */
    private synchronized Long getSoNbr(String sequence) {
        return querySequence(sequence);
    }

    public Long getSoNbr(SequenceItem sequenceItem) {

        return getSoNbr(sequenceItem.getValue());
    }
    /**
     * 查询sequence获取so_nbr
     * @param sequenceName
     * @return
     */
    private Long querySequence(String sequenceName) {

        long soNbr = 0L;
        long newSoNbr = 0L;
        long limit = 0L;

        // 先尝试从缓存中获取，如果缓存中没有再进行查询
        Long[] queue = sequenceMap.get(sequenceName);
        if (queue != null) {

            long cacheSize = queue[1];
            if (cacheSize > 0) {
                soNbr = queue[0] + 1;
                cacheSize --;
                queue[0] = soNbr;
                queue[1] = cacheSize;
                sequenceMap.put(sequenceName, queue);
                return soNbr;
            }
        }

        synchronized (this) {

            // 此时再次获取如果没有才能查询数据库
            Long[] queueRe = sequenceMap.get(sequenceName);
            if (queueRe != null) {

                long cacheSize = queueRe[1];
                if (cacheSize > 0) {
                    soNbr = queueRe[0] + 1;
                    cacheSize --;
                    queue[0] = soNbr;
                    queue[1] = cacheSize;
                    sequenceMap.put(sequenceName, queueRe);
                    return soNbr;
                }
            }
            
            // 1、获取事务管理器
            PlatformTransactionManager transactionManager = (PlatformTransactionManager) SpringContextHolder.getBean(PlatformTransactionManager.class);
            // 2、获取事务定义
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 3、设置事务传播行为，开启新事务
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            TransactionStatus transaction = transactionManager.getTransaction(def);

            try {
                SysSequence sysSequence = new SysSequence();
                // 在sql添加一个随机数用来破坏mybatis一级缓存的缓存机制
                double random = Math.random();
                sysSequence = sysSequenceMapper.getSequences(sequenceName, random + "");
                soNbr = sysSequence.getSequenceValue();
                limit = sysSequence.getLimit();
                newSoNbr = soNbr + limit + 1; // +1的目的是为了缓存获取sequence_value没有从0开始，都是从1开始的。
                sysSequence.setSequenceValue(newSoNbr);
                sysSequence.setUpdateDate(new Date());
                sysSequenceMapper.updateByCode(sysSequence);
                transactionManager.commit(transaction);
                Long[] entity = new Long[2];
                entity[0] = soNbr;
                entity[1] = limit;
                sequenceMap.put(sequenceName, entity);
            } catch (Exception e) {
                transactionManager.rollback(transaction);
                log.error("get sequence error", e);
            }
        }
        return soNbr;
    }

}
